/*option to verbose error description: %error-verbose*/
%error-verbose
%{
    #include <stdio.h>   
    #include <stdlib.h>
    #include <string.h>
    #include <math.h>
    #include <limits.h>
    #include <float.h>
    #include "mystruct.h"
    #include "gen_code.h"
    extern int yylineno;
    extern int yyparse();

FILE* output;

char var_name[1030][1024];
Variable var[1030][10];
int var_num = 1;
int prev_err_lineno = 0;
int prev_err_no = 0;

struct function_info function[100];
int  function_num = 0;
int  in_function = -1;

int global_addr = 1;
int local_addr = 5000;
int function_addr = 10000;
int scope = 0;
%}

%union {
    char* sValue;
    int cValue;
    int neg_type;
    int r_type;
    struct Number constant;
    struct array_def arrayDef;
    struct id_def idDef;
    int label_index;
    struct else_attr else_type;
    struct if_attr if_type;
    struct while_attr while_type;
    struct for_attr for_type;
    struct switch_attr switch_type;
    struct int_var_attr int_var_type;
    otherwise_attr otherwise_type;
    struct arraydim_type arraydim_info;
    struct function_param function_param_info; 
    int function_pos;
    int function_decl;
    int function_def_type;
    int primitive_type;
    struct arg_info arg_type;
}

%start PROGRAMSTART

%type <neg_type>negsign
%type <constant>arithExpr
%type <arrayDef>ArrayList
%type <idDef>IdList
%type <label_index>logicExpr
%type <else_type>elseBlock
%type <if_type>IF
%type <while_type>WHILE
%type <for_type>FOR
%type <switch_type>caseBlock
%type <switch_type>SWITCH
%type <otherwise_type>otherwiseBlock
%type <otherwise_type>OTHERWISE
%type <primitive_type>primitiveType
%type <arraydim_info>arraydim
%type <function_decl>functionDeclInit
%type <function_param_info>paramDeclInit
%type <function_param_info>paramIdList
%type <r_type>functionType
%type <function_def_type>functionDef
%type <arg_type>functionArg

%token <cValue>CHAR_LITERAL
%token <sValue>ID
%token <sValue>NUM
%token <sValue>FLOAT
%token PROGRAM VAR ENDVAR BEGINRE READ WRITE WRITESP WRITELN END
%token OR AND NOT
%token GREATERTHAN LESSTHAN GREATERTHANEQ LESSTHANEQ EQUAL NOTEQUAL
%token ASSIGN
%token IF ELSE THEN ENDIF
%token INTEGER REAL CHAR
%token ARRAY RANGEFROMTO OF
%token FOR TO DOWNTO DO
%token WHILE
%token FUNCTION PROCEDURE
%token SWITCH OTHERWISE ENDCASE

%left  OR
%left  AND
%right ASSIGN
%left  EQUAL NOTEQUAL
%left  GREATERTHAN LESSTHAN GREATERTHANEQ LESSTHANEQ
%left  '+' '-'
%left  '*' '/'
%right POS NEG NOT
%%
PROGRAMSTART: PROGRAM ID
	    {
		strcpy(var_name[0],$2);
	    } 
	    VAR varDeclInit ENDVAR functionDeclInit
	    {
		gen_main(output);
	    	gen_allocated_space(output,global_addr);
	    }
	    blockstmt
	    {
		gen_end(output);
	    }
	    | PROGRAM ID
	    {
		gen_main(output);
	    }blockstmt
	    {
		gen_end(output);
	    }
	    ;

primitiveType: INTEGER
	     {
		$$ = INT_TYPE;
	     }
	     | REAL
	     {
		$$ = FLOAT_TYPE;
	     }
	     | CHAR
	     {
		$$ = CHAR_TYPE;
	     }
	     ;

varDeclInit: primitiveType ':' ID IdList ';'
	   {
		int i;
		int status = 0;
		//look up symbol table to find the index of variable
		int var_index = CheckDeclared($3,scope,&status);
		if(var_index==-1)
		{
		        strcpy(var_name[var_num],$3);
			var[var_num][scope].type = $1;
			if(scope==0)
			{
				var[var_num][scope].addr = global_addr;
				global_addr++;
			}
			else
			{
				var[var_num][scope].addr = local_addr;
				local_addr++;
			}
			for(i=0;i<$4.var_num;i++)
			{
			    int index = $4.var_index[i];
			    var[index][scope].type = $1;
			}
			var_num++;
		}
		else if(status==UNDECLARED_IN_THIS_SCOPE)
		{
			var[var_index][scope].type = $1;
			if(scope==0)
			{
				var[var_index][scope].addr = global_addr;
				global_addr++;
			}
			else
			{
				var[var_index][scope].addr = local_addr;
				local_addr++;
			}
			for(i=0;i<$4.var_num;i++)
			{
			    int index = $4.var_index[i];
			    var[index][scope].type = $1;
			}
		}
		else
		{
		    yyerror("variable declared twice");
		}
	   } varDeclInit
	   | ARRAY '[' negsign NUM RANGEFROMTO negsign NUM arraydim ']' OF primitiveType ':' ID ArrayList ';'
	   {
		int i,j;
		int status = 0;
		int type;
		switch($11)
		{
		    case INT_TYPE:
			type = INT_ARRAY_TYPE;
			break;
		    case FLOAT_TYPE:	
			type = FLOAT_ARRAY_TYPE;
			break;
		    case CHAR_TYPE:	
			type = CHAR_ARRAY_TYPE;
			break;
		}
		//look up symbol table to find the index of variable
		int var_index = CheckDeclared($13,scope,&status);
		$8.array_start[0] = atoi($4);
		$8.array_end[0] = atoi($7);
		if($3==1){$8.array_start[0] = -$8.array_start[0];}
		if($6==1){$8.array_end[0] = -$8.array_end[0];}
		//variable was undecalred
		if(var_index==-1)
		{
		        strcpy(var_name[var_num],$13);	
			var[var_num][scope].type = type;
			var[var_num][scope].dim  = $8.dim;
			int allocated_space = 1;
			//initialize ID as array type
			for(i=0;i<$8.dim;i++)
			{
			    var[var_num][scope].array_start[i] = $8.array_start[i];
			    var[var_num][scope].array_end[i] = $8.array_end[i];
			    allocated_space = allocated_space * ($8.array_end[i] - $8.array_start[i] + 1);
			}
			if(scope==0)
			{
				var[var_num][scope].addr = global_addr;
				global_addr = global_addr + allocated_space;
			}
			else
			{
				var[var_num][scope].addr = local_addr;
				local_addr = local_addr + allocated_space;
			}
			var_num++;
		}
		else if(status==UNDECLARED_IN_THIS_SCOPE)
		{
			var[var_index][scope].type  = type;
			var[var_index][scope].dim  = $8.dim;
			int allocated_space = 0;
			//initialize ID as array type
			for(i=0;i<$8.dim;i++)
			{
			    var[var_index][scope].array_start[i] = $8.array_start[i];
			    var[var_index][scope].array_end[i] = $8.array_end[i];
			    allocated_space = allocated_space * ($8.array_end[i] - $8.array_start[i] + 1);
			}
			if(scope==0)
			{
				var[var_index][scope].addr = global_addr;
				global_addr = global_addr + allocated_space;
			}
			else
			{
				var[var_index][scope].addr = local_addr;
				local_addr = local_addr + allocated_space;;
			}
		}
		else
		{
		    yyerror("variable declared twice");
		}
		//use the information in ArrayList to initialize arrays
		for(i=0;i<$14.array_var_num;i++)
		{
			int index = $14.var_index[i];
			int allocated_space = 0;
			var[index][scope].type = type;
			for(j=0;j<$8.dim;j++)
			{
			    var[index][scope].array_start[j] = $8.array_start[j];
			    var[index][scope].array_end[j] = $8.array_end[j];
			    allocated_space = allocated_space * ( $8.array_end[j] - $8.array_start[j] + 1);
			}
			if(scope==0)
			{
				var[index][scope].addr = global_addr;
				global_addr = global_addr + allocated_space;
			}
			else
			{
				var[index][scope].addr = local_addr;
				local_addr = local_addr + allocated_space;
			}
		}
	   } varDeclInit
	   | /*NULL*/
	   ;

negsign    : '-'
	   {
	    $$ = 1;
	   }
	   | /*NULL*/
	   {
	    $$ = 0;
	   }
	   ;

arraydim   : ',' negsign NUM RANGEFROMTO negsign NUM arraydim
	   {
	     $$ = $7;
	     int start = atoi($3);
	     int end = atoi($6);
	     if($2==1)start = -start;
	     if($5==1)end = -end;
	     int dim = $$.dim;
	     $$.array_start[dim] = start;
	     $$.array_end[dim] = end;
	     $$.dim++;
	   }
	   | /*NULL*/
	   {
	     $$.dim = 1;
	   }
	   ;

IdList  : ',' ID IdList
	   {
		$$ = $3;
		int status = 0;
		int var_index = CheckDeclared($2,scope,&status);
		// if the name of ID was not in use, then add an entry to symbol table and do initialization
		if(var_index==-1)
		{
			$$.var_index[$$.var_num] = var_num;
			$$.var_num++;
		        strcpy(var_name[var_num],$2);
			if(scope==0)
			{
				var[var_num][scope].addr = global_addr;
				global_addr++;
			}
			else
			{
				var[var_num][scope].addr = local_addr;
				local_addr++;
			}
			var_num++;
		}
		// if the name of ID was in use, but ID was not declared at current scope,  then add an entry to the chain of this variable and do initialization
		else if(status==UNDECLARED_IN_THIS_SCOPE)
		{
			$$.var_index[$$.var_num] = var_index;
			$$.var_num++;
			if(scope==0)
			{
				var[var_index][scope].addr = global_addr;
				global_addr++;
			}
			else
			{
				var[var_index][scope].addr = local_addr;
				local_addr++;
			}
		}
	        else if(var_index==0)
	        {
		    $$ = $3;
		    yyerror("invlid ID name");
	        }
		else
		{
		    $$ = $3;
		    yyerror("variable declared twice");
		}
	   }
	   | /*NULL*/
	   {
	     $$.var_num = 0;
	   }
	   ;

ArrayList : ',' ID ArrayList
	     {
		//collect the information of declared arrays
		int status = 0;
		int var_index = CheckDeclared($2,scope,&status);
		if(var_index==-1)
		{
		    $$ = $3;
		    strcpy(var_name[var_num],$2);
		    $$.var_index[$$.array_var_num] = var_num;
		    $$.array_var_num++;
		    var_num++;
		}
		else if(status==UNDECLARED_IN_THIS_SCOPE)
		{
		    $$ = $3;
		    $$.var_index[$$.array_var_num] = var_index;
		    $$.array_var_num++;
		}
	        else if(var_index==0)
	        {
		    $$ = $3;
		    yyerror("invlid ID name");
	        }
		else
		{
		    $$ = $3;
		    yyerror("variable declared twice");
		}
	     }
	     | /*NULL*/
	     {
		$$.array_var_num = 0;
	     }
	     ;

functionDef  : PROCEDURE
	     {
		$$ = PROCEDURE_TYPE;
	     }
	     | FUNCTION
	     {
		$$ = FUNCTION_TYPE;
	     }
	     ;

functionDeclInit  : functionType functionDef ID '(' paramDeclInit ')' ';'
		  {
		    in_function = function_num;
		    strcpy(function[function_num].name,$3);
		    function[function_num].return_type = $1;
		    function[function_num].param_n = $5.param_n;
		    //record the information of function
		    if($2==FUNCTION_TYPE)
		    {
			function[function_num].return_addr = function_addr;
			function_addr++;
		    }
		    int i;
		    //record the information of parameters of this function
		    for(i=0;i<$5.param_n;i++)
		    {
			function[function_num].param_type[i] = $5.type[i];
			strcpy(function[function_num].param_name[i],$5.name[i]);
			function[function_num].param_addr[i] = function_addr;
			function_addr++;
		    }
		    fprintf(output,"%s()\n",$3);
		    fprintf(output,"{\n");
		    scope++;
		    function_num++;
		  }varblock blockstmt
		  {
		    //end of this function
		    fprintf(output,"\t;\n");
		    fprintf(output,"}\n");
		    in_function = -1;
		    int i = 0;
		    for(i=0;i<var_num;i++)
		    {
			var[i][scope].addr = 0;
		    }
		    scope--;
		  }functionDeclInit
		  {
		    $$ = 1;
		  }
		  | /*NULL*/
		  {
		    $$ = 0;
		  }
		  ;

functionType      : INTEGER
		  {
		    $$ = INT_TYPE;
		  }
		  | REAL
		  {
		    $$ = FLOAT_TYPE;
		  }
		  | /*NULL*/
		  {
		    $$ = VOID_TYPE;
		  }
		  ;

paramDeclInit       : ID paramIdList ':' INTEGER ';' paramDeclInit
		    {
			//collect paramter information
			$$ = $2;
			strcpy($$.name[$$.param_n],$1);
			$$.param_n++;
			int i;
			for(i=0;i<$$.param_n;i++)
			{
			    $$.type[i] = INT_TYPE;
			}
			int ori_param_n = $$.param_n;
			$$.param_n = $$.param_n + $6.param_n;
			for(i=ori_param_n;i<$$.param_n;i++)
			{
			    $$.type[i] = $6.type[i-ori_param_n];
			}
		    }
		    | ID paramIdList ':' REAL ';' paramDeclInit
		    {
			//collect paramter information
			$$ = $2;
			strcpy($$.name[$$.param_n],$1);
			$$.param_n++;
			int i;
			for(i=0;i<$$.param_n;i++)
			{
			    $$.type[i] = FLOAT_TYPE;
			}
			int ori_param_n = $$.param_n;
			$$.param_n = $$.param_n + $6.param_n;
			for(i=ori_param_n;i<$$.param_n;i++)
			{
			    $$.type[i] = $6.type[i-ori_param_n];
			}
		    }
		    | /*NULL*/
		    {
		       $$.param_n = 0;
		    }
		    ;

paramIdList         : ',' ID paramIdList
		    {
			//collect paramter information
			$$ = $3;
			strcpy($$.name[$$.param_n],$2);
			$$.param_n++;
		    }
		    | /*NULL*/
		    {
			$$.param_n = 0;
		    }
		    ;

blockstmt  : BEGINRE{scope++;} varblock stmtList END
	   { 
	     int i;
	     for(i=0;i<var_num;i++)
	     {
		var[i][scope].addr = 0;
	     }
	     scope--;
	   }
	   | stmt
	   | /*NULL**/
	   ;

varblock   : VAR varDeclInit ENDVAR
	   | /*NULL*/
	   ;

stmtList   :  BEGINRE{scope++;} varblock stmtList END
	   {
	     int i;
	     for(i=0;i<var_num;i++)
	     {
		var[i][scope].addr = 0;
	     }
	     scope--;
	   } stmtList
	   | stmt stmtList
	   | /*NULL*/
	   ;

arrayindex : ',' arithExpr arrayindex
	   {
	     if($2.type!=INT_TYPE)
	     {
		yyerror("invalid array index");
	     }
	   }
	   | /*NULL*/
	   ;

stmt       : ID '[' arithExpr arrayindex ']' ASSIGN arithExpr ';'
	   {
	     int var_index = CheckDeclared($1,scope,NULL);
	     if(var_index>=1)
	     {
		if($3.type==INT_TYPE)
		{
		    int used_scope = 0;
		    int array_pointer = find_allocated_space(var_num,var_index,scope,&used_scope);
		    Variable temp = var[var_index][used_scope];
		    if(temp.type==INT_ARRAY_TYPE||temp.type==CHAR_ARRAY_TYPE)
		    {
			if($7.type!=INT_TYPE&&$7.type!=UNKNOWN_TYPE)yyerror("mismatched data type");
			gen_code_assign_array(output,array_pointer,temp.array_start,temp.array_end,INT_TYPE,temp.dim);
		    }
		    else if(temp.type==FLOAT_ARRAY_TYPE)
		    {
			if($7.type!=FLOAT_TYPE&&$7.type!=UNKNOWN_TYPE)yyerror("mismatched data type");
			gen_code_assign_array(output,array_pointer,temp.array_start,temp.array_end,FLOAT_TYPE,temp.dim);
		    }
		    else
		    {
			char buf[1000];
			sprintf(buf,"variable %s was not declared as array",$1);
			yyerror(buf);
		    }
		}
	        else if(var_index==0)
	        {
		  yyerror("invlid ID name");
	        }
		else
		{
		    yyerror("invalid array index");
		}
	     }
	   }
	   | ID ASSIGN arithExpr ';'
	   {
	     int find_function = -1;
	     int i;
	     if(in_function>=0)
	     {
		int f_index = -1;
		for(i=0;i<function_num;i++)
		{
		    if(strcmp(function[i].name,$1)==0)
		    {
			find_function = 1;
			f_index = i;
			break;
		    }
		}
		if(f_index>=0)
		{
		    if(function[f_index].return_type==INT_TYPE)
		    {
			gen_code(output,ASSIGN_INT,function[f_index].return_addr);
		    }
		    else if(function[f_index].return_type==FLOAT_TYPE)
		    {
			gen_code(output,ASSIGN_FLOAT,function[f_index].return_addr);
		    }
		    else
		    {
			yyerror("function has no return type");
		    }
		}
	     }
	     if(find_function==-1)
	     {
		int var_index = CheckDeclared($1,scope,NULL);
		if(var_index>=1)
		{
		    int used_scope;
		    int pos = find_allocated_space(var_num, var_index,scope,&used_scope);
		    if(var[var_index][used_scope].type==INT_TYPE||var[var_index][used_scope].type==CHAR_TYPE)
		    {
			if($3.type!=INT_TYPE&&$3.type!=UNKNOWN_TYPE)yyerror("mismatched data type");
			gen_code(output,ASSIGN_INT,pos);
		    }
		    else if(var[var_index][used_scope].type==FLOAT_TYPE)
		    {
			if($3.type!=FLOAT_TYPE&&$3.type!=UNKNOWN_TYPE)yyerror("mismatched data type");
			gen_code(output,ASSIGN_FLOAT,pos);
		    }
		    else
		    {
			char buf[1000];
			sprintf(buf,"variable %s was declared as array",$1);
			yyerror(buf);
		    }
		}
	        else if(var_index==0)
	        {
		    yyerror("invlid ID name");
	        }
		else
		{
		    yyerror("undeclared variable");
		}
	     }
	   }
	   | READ '(' ID ')' ';'
	   {
	      int var_index = CheckDeclared($3,scope,NULL);
	      if(var_index>=1)
	      {
		int used_scope = 0;
		int pos = find_allocated_space(var_num, var_index,scope,&used_scope);
		int type = var[var_index][used_scope].type;
		if(type==INT_TYPE)
		{
		    gen_code(output,READ_INT,pos);
		}
		else if(type==FLOAT_TYPE)
		{
		    gen_code(output,READ_FLOAT,pos);
		}
		else if(type==CHAR_TYPE)
		{
		    gen_code(output,READ_CHAR,pos);
		}
		else
		{
		    char buf[1000];
		    sprintf(buf,"variable %s was declared as array",$3);
		    yyerror(buf);
		}
	      }
	      else if(var_index==0)
	      {
		yyerror("invlid ID name");
	      }
	      else
	      {
		yyerror("undeclared variable");
	      }
	   }
	   | READ '(' ID '[' arithExpr arrayindex ']' ')' ';'
	   {
	     int var_index = CheckDeclared($3,scope,NULL);
	     if(var_index>=1)
	     {
		if($5.type==INT_TYPE)
		{
		    int used_scope = 0;
		    int array_pointer = find_allocated_space(var_num, var_index,scope,&used_scope);
		    Variable temp = var[var_index][used_scope];
		    if(var[var_index][used_scope].type==INT_ARRAY_TYPE)
		    {
			gen_code_read_array(output,array_pointer,temp.array_start,temp.array_end,INT_TYPE,temp.dim);
		    }
		    else if(temp.type==CHAR_ARRAY_TYPE)
		    {
			gen_code_read_array(output,array_pointer,temp.array_start,temp.array_end,CHAR_TYPE,temp.dim);
		    }
		    else if(var[var_index][used_scope].type==FLOAT_ARRAY_TYPE)
		    {
			gen_code_read_array(output,array_pointer,temp.array_start,temp.array_end,FLOAT_TYPE,temp.dim);
		    }
		    else
		    {
			char buf[1000];
			sprintf(buf,"variable %s was not declared as array",$3);
			yyerror(buf);
		    }
		}
		else
		{
		    yyerror("invalid array index");
		}
	     }
	     else
	     {
		yyerror("undeclared variable");
	     }
	   }
	   | WRITE '(' ID ')' ';'
	   {
	    int find_param = 0;
	    if(in_function>=0)
	    {
		int i;
		int index = -1;
		for(i=0;i<function[in_function].param_n;i++)
		{
		    if(strcmp(function[in_function].param_name[i],$3)==0)
		    {
			index = i;
			find_param = 1;
			break;
		    }
		}
		if(index>=0)
		{
		    if(function[in_function].param_type[index]==INT_TYPE)
		    {
			gen_code(output,WRITE_INT,function[in_function].param_addr[index]);
		    }
		    else
		    {
			gen_code(output,WRITE_FLOAT,function[in_function].param_addr[index]);	
		    }
		}
	    }
	    if(find_param==0)
	    {
		int var_index = CheckDeclared($3,scope,NULL);
		if(var_index>=1)
		{
		    int used_scope = 0;
		    int pos = find_allocated_space(var_num,var_index,scope,&used_scope);
		    if(var[var_index][used_scope].type==INT_TYPE)
		    {
			gen_code(output,WRITE_INT,pos);
		    }
		    else if(var[var_index][used_scope].type==FLOAT_TYPE)
		    {
			gen_code(output,WRITE_FLOAT,pos);
		    }
		    else if(var[var_index][used_scope].type==CHAR_TYPE)
		    {
			gen_code(output,WRITE_CHAR,pos);
		    }
		    else
		    {
			char buf[1000];
			sprintf(buf,"variable %s was declared as array",$3);
			yyerror(buf);
		    }
		}
		else
		{
		    yyerror("undeclared variable");
		}
	    }
	   }
	   | WRITE '(' ID '[' arithExpr arrayindex ']' ')' ';'
	   {
	     int var_index = CheckDeclared($3,scope,NULL);
	     if(var_index>=1)
	     {
		if($5.type==INT_TYPE)
		{
		    int used_scope = 0;
		    int array_pointer = find_allocated_space(var_num,var_index,scope,&used_scope);
		    Variable temp = var[var_index][used_scope];
		    if(var[var_index][used_scope].type==INT_ARRAY_TYPE)
		    {
			gen_code_write_array(output,array_pointer,temp.array_start,temp.array_end,INT_TYPE,temp.dim);
		    }
		    else if(temp.type==CHAR_ARRAY_TYPE)
		    {
			gen_code_write_array(output,array_pointer,temp.array_start,temp.array_end,CHAR_TYPE,temp.dim);
		    }
		    else if(var[var_index][used_scope].type==FLOAT_ARRAY_TYPE)
		    {
			gen_code_write_array(output,array_pointer,temp.array_start,temp.array_end,FLOAT_TYPE,temp.dim);
		    }
		    else
		    {
			char buf[1000];
			sprintf(buf,"variable %s was not declared as array",$3);
			yyerror(buf);
		    }
		}
		else
		{
		    yyerror("invalid array index");
		}
	     }
	   }
	   | WRITESP '(' ')' ';'
	   {
	      gen_code(output,WRITE_SP,0);
	   }
	   | WRITELN '(' ')' ';'
	   {
	      gen_code(output,WRITE_LN,0);
	   }
	   | IF logicExpr THEN 
	   {
	     $1.false_label = label_num;
	     label_num++;
	     $1.true_label = label_num;
	     label_num++;
	     gen_code(output,IF_LABEL,$1.false_label);
	   } blockstmt elseBlock
	   {
	     if($6.type==ELSE_TYPE)
	     {
		gen_code(output,GOTO,$1.true_label);
		gen_code(output,GEN_LABEL,$1.false_label);
	     }
	   }blockstmt ENDIF
	   {
	     if($6.type==THEN_TYPE)
	     {
		gen_code(output,GEN_LABEL,$1.false_label);
	     }
	     else
	     {
		gen_code(output,GEN_LABEL,$1.true_label);
	     }
	   }
	   | FOR ID ASSIGN arithExpr
	   {
	     int status;
	     int var_index = CheckDeclared($2,scope,&status);
	     if(var_index==-1)yyerror("undeclared variable");
	     int used_scope;
	     int pos = find_allocated_space(var_num,var_index,scope,&used_scope);
	     if(var[var_index][used_scope].type!=INT_TYPE)yyerror("mismatched data type");
	     $1.id_pos = pos;
	     $1.true_label = label_num;
	     label_num++;
	     $1.false_label = label_num;
	     label_num++;
	     gen_code(output,ASSIGN_INT,pos);
	   }TO
	   {
	    gen_code(output,GEN_LABEL,$1.true_label);
	   }arithExpr 
	   {
	     gen_code(output,GET_INT,$1.id_pos);
	     gen_code(output,GREATER_THAN_EQ_INT,0);
	     gen_code(output,IF_LABEL,$1.false_label);
	   }DO blockstmt
	   {
	    gen_code(output,ADD_INT_1,$1.id_pos);
	     gen_code(output,GOTO,$1.true_label);
	     gen_code(output,GEN_LABEL,$1.false_label);
	   }
	   | FOR ID ASSIGN arithExpr
	   {
	     int status;
	     int var_index = CheckDeclared($2,scope,&status);
	     if(var_index==-1)yyerror("undeclared variable");
	     int used_scope;
	     int pos = find_allocated_space(var_num,var_index,scope,&used_scope);
	     if(var[var_index][used_scope].type!=INT_TYPE)yyerror("mismatched data type");
	     $1.id_pos = pos;
	     $1.true_label = label_num;
	     label_num++;
	     $1.false_label = label_num;
	     label_num++;
	     gen_code(output,ASSIGN_INT,pos);
	   }DOWNTO
	   {
	    gen_code(output,GEN_LABEL,$1.true_label);
	   }arithExpr 
	   {
	     gen_code(output,GET_INT,$1.id_pos);
	     gen_code(output,LESS_THAN_EQ_INT,0);
	     gen_code(output,IF_LABEL,$1.false_label);
	   }DO blockstmt
	   {
	     gen_code(output,SUB_INT_1,$1.id_pos);
	     gen_code(output,GOTO,$1.true_label);
	     gen_code(output,GEN_LABEL,$1.false_label);
	   }
	   | WHILE
	   {
	     $1.false_label = label_num;
	     label_num++;
	     $1.true_label = label_num;
	     label_num++;
	     gen_code(output,GEN_LABEL,$1.true_label);
	   }logicExpr 
	   {
	     gen_code(output,IF_LABEL,$1.false_label);
	   }DO blockstmt
	   {
	     gen_code(output,GOTO,$1.true_label);
	     gen_code(output,GEN_LABEL,$1.false_label);
	   }
	   | SWITCH arithExpr OF 
	   {
	     $1.test_label = label_num;
	     label_num++;
	     gen_code(output,GOTO,$1.test_label);
	   }caseBlock otherwiseBlock
	   {
	     if($6.otherwise_label!=0)
	     {
		gen_code(output,GOTO,$5.next_label);
	     }
	     gen_code(output,GEN_LABEL,$1.test_label);
	     int i;
	     if($5.type==INT_TYPE)
	     {
		fprintf(output,"\tI__0 = POP__S();\n");
		for(i=0;i<$5.label_num;i++)
		{
		    fprintf(output,"\tif(I__0==%d) goto L%d;\n",$5.iconst[i],$5.labels[i]);
		}
	     }
	     else
	     {
		fprintf(output,"\tF__0 = FPOP__S();\n");
		for(i=0;i<$5.label_num;i++)
		{
		    fprintf(output,"\tif(F__0==%f) goto L%d;\n",$5.fconst[i],$5.labels[i]);
		}
	     }
	     if($6.otherwise_label!=0)
	     {
		gen_code(output,GOTO,$6.otherwise_label);
	     }
	   }ENDCASE ';'
	   {
	     gen_code(output,GEN_LABEL,$5.next_label);
	   }
	   | ID '(' functionArg ')' ';'
	   {
	     int f_index = -1;
	     int i;
	     for(i=0;i<function_num;i++)
	     {
		if(strcmp(function[i].name,$1)==0)
		{
		    f_index = i;
		    break;
		}
	     }
	     if(f_index==-1)
	     {
		yyerror("undeclared function name");
	     }
	     else
	     {
		if(function[f_index].param_n!=$3.arg_n)yyerror("wrong number of arguments");
		for(i<function[f_index].param_n-1;i>=0;i--)
		{
		    if(function[f_index].param_type[i]!=$3.type[i])yyerror("wrong argument type");
		    if(function[f_index].param_type[i]==INT_TYPE)
		    {
			fprintf(output,"\tI__0 = POP__S();\n");//arg_value
			fprintf(output,"\tI__1 = %d;\n",function[f_index].param_addr[i]);
			fprintf(output,"\tASSET__S(I__1,I__0);\n");
		    }
		    else
		    {
			fprintf(output,"\tF__0 = FPOP__S();\n");//arg_value
			fprintf(output,"\tI__1 = %d;\n",function[f_index].param_addr[i]);
			fprintf(output,"\tFASSET__S(I__1,F__0);\n");
		    }
		}
		fprintf(output,"\t%s();\n",$1);
	     }
	   }
	   ;

functionArg : arithExpr
	    {
		$$.arg_n = 1;
		$$.type[0] = $1.type;
	    }
	    | arithExpr ',' functionArg
	    {
		$$ = $3;
		int arg = $3.arg_n;
		$$.type[arg] = $1.type;
		$$.arg_n++;
	    }
	    | /*NULL*/
	    {
		$$.arg_n = 0;
	    }
	    ;

caseBlock  : caseBlock NUM ':'
	   {
	     int case_label_num = $1.label_num;
	     $1.labels[case_label_num] = label_num;
	     $1.iconst[case_label_num] = atoi($2);
	     gen_code(output,GEN_LABEL,label_num);
	     $1.label_num++;
	     label_num++;
	   }
	   blockstmt
	   {
	     gen_code(output,GOTO,$1.next_label);
	     $1.type = INT_TYPE;
	     $$ = $1;
	   }
	   | caseBlock FLOAT ':'
	   {
	     int case_label_num = $1.label_num;
	     $1.labels[case_label_num] = label_num;
	     $1.fconst[case_label_num] = atof($2);
	     gen_code(output,GEN_LABEL,label_num);
	     $1.label_num++;
	     label_num++;
	   }
	   blockstmt
	   {
	     gen_code(output,GOTO,$1.next_label);
	     $1.type = FLOAT_TYPE;
	     $$ = $1;
	   }
	   | /*NULL*/
	   {
	     $$.next_label = label_num;
	     label_num++;
	   }
	   ;

otherwiseBlock : OTHERWISE ':'
	       {
	         $1.otherwise_label = label_num;
		 label_num++;
		 gen_code(output,GEN_LABEL,$1.otherwise_label);
	       }blockstmt
	       {
		 $$ = $1;
	       }
	       | /*NULL*/
	       {
		 $$.otherwise_label = 0;
	       }
	       ;

elseBlock  : ELSE
	     {
	       $$.type = ELSE_TYPE;
	     }
	     | /*NULL*/
	     {
	       $$.type = THEN_TYPE;
	     }
	     ;

arithExpr  : arithExpr '+' arithExpr 
	   {
	    if($1.type==INT_TYPE && $3.type==INT_TYPE)
	    {
		$$.type = INT_TYPE;
		gen_code(output,ADD_INT,0);
	    }
	    else if($1.type==FLOAT_TYPE && $3.type==FLOAT_TYPE)
	    {
		$$.type = FLOAT_TYPE;
		gen_code(output,ADD_FLOAT,0);
	    }
	    else if($1.type==UNKNOWN_TYPE||$3.type==UNKNOWN_TYPE)
	    {
		$$.type = UNKNOWN_TYPE;
	    }
	    else if($1.type!=$3.type)
	    {
		yyerror("mismatched data type");
	    }
	   }
	   | arithExpr '-' arithExpr 
	   {
	    if($1.type==INT_TYPE && $3.type==INT_TYPE)
	    {
		$$.type = INT_TYPE;
		gen_code(output,SUB_INT,0);
	    }
	    else if($1.type==FLOAT_TYPE && $3.type==FLOAT_TYPE)
	    {
		$$.type = FLOAT_TYPE;
		gen_code(output,SUB_FLOAT,0);
	    }
	    else if($1.type==UNKNOWN_TYPE||$3.type==UNKNOWN_TYPE)
	    {
		$$.type = UNKNOWN_TYPE;
	    }
	    else if($1.type!=$3.type)
	    {
		yyerror("mismatched data type");
	    }
	   }
	   | arithExpr '*' arithExpr 
	   {
	    if($1.type==INT_TYPE && $3.type==INT_TYPE)
	    {
		$$.type = INT_TYPE;
		gen_code(output,MUL_INT,0);
	    }
	    else if($1.type==FLOAT_TYPE && $3.type==FLOAT_TYPE)
	    {
		$$.type = FLOAT_TYPE;
		gen_code(output,MUL_FLOAT,0);
	    }
	    else if($1.type==UNKNOWN_TYPE||$3.type==UNKNOWN_TYPE)
	    {
		$$.type = UNKNOWN_TYPE;
	    }
	    else if($1.type!=$3.type)
	    {
		yyerror("mismatched data type");
	    }
	   }
	   | arithExpr '/' arithExpr 
	   {
	    if($1.type==INT_TYPE && $3.type==INT_TYPE)
	    {
		$$.type = INT_TYPE;
		gen_code(output,DIV_INT,0);
	    }
	    else if($1.type==FLOAT_TYPE && $3.type==FLOAT_TYPE)
	    {
		$$.type = FLOAT_TYPE;
		gen_code(output,DIV_FLOAT,0);
	    }
	    else if($1.type==UNKNOWN_TYPE||$3.type==UNKNOWN_TYPE)
	    {
		$$.type = UNKNOWN_TYPE;
	    }
	    else if($1.type!=$3.type)
	    {
		yyerror("mismatched data type");
	    }
	   }
	   | '+' arithExpr %prec POS 
	   {
	    if($2.type == INT_TYPE)
	    {
		$$.type = INT_TYPE;
		gen_code(output,POS_INT,0);
	    }
	    else if($2.type == FLOAT_TYPE)
	    {
		$$.type = FLOAT_TYPE;
		gen_code(output,POS_FLOAT,0);
	    }
	    else
	    {
		$$.type = $2.type;
	    }
	   }
	   | '-' arithExpr %prec NEG 
	   {
	    if($2.type ==INT_TYPE)
	    {
		$$.type = INT_TYPE;
		gen_code(output,NEG_INT,0);
	    }
	    else if($2.type == FLOAT_TYPE)
	    {
		$$.type = FLOAT_TYPE;
		gen_code(output,NEG_FLOAT,0);
	    }
	    else
	    {
		$$.type = $2.type;
	    }
	   }
	   | NUM                     
	   {
	    $$.type = INT_TYPE;
	    int ival = atoi($1);
	    if(ival==INT_MAX||ival==INT_MIN)
	    {
		yyerror("invalid constant");
	    }
	    gen_code(output,GEN_NUM,atoi($1));
	   }
	   | FLOAT
	   {
	    $$.type = FLOAT_TYPE;
	    float fval = strtof($1,NULL);
	    if(fval==FLT_MAX||fval==FLT_MIN)
	    {
		yyerror("invalid constant");
	    }
	    gen_float(output,strtof($1,NULL));
	   }
	   | '(' arithExpr ')'
	   {
	    $$.type = $2.type;
	   }
	   | CHAR_LITERAL
	   {
	     $$.type = INT_TYPE;
	     gen_code(output,GEN_NUM,$1);
	   }
	   | ID			     
	   {
	    int find_param = 0;
	    if(in_function>=0)
	    {
		int i;
		int index = -1;
		for(i=0;i<function[in_function].param_n;i++)
		{
		    if(strcmp(function[in_function].param_name[i],$1)==0)
		    {
			index = i;
			find_param = 1;
			break;
		    }
		}
		if(index>=0)
		{
		    if(function[in_function].param_type[index]==INT_TYPE)
		    {
			gen_code(output,GET_INT,function[in_function].param_addr[index]);
			$$.type = INT_TYPE;
		    }
		    else
		    {
			gen_code(output,GET_FLOAT,function[in_function].param_addr[index]);	
			$$.type = FLOAT_TYPE;
		    }
		}
	    }
	    if(find_param==0)
	    {
		int var_index = CheckDeclared($1,scope,NULL);
		if(var_index>=1)
		{
		    int used_scope;
		    int pos = find_allocated_space(var_num, var_index,scope,&used_scope);
		    if(var[var_index][used_scope].type==INT_TYPE||var[var_index][used_scope].type==CHAR_TYPE)
		    {
			$$.type = INT_TYPE;
			gen_code(output,GET_INT,pos);
		    }
		    else if(var[var_index][used_scope].type==FLOAT_TYPE)
		    {
			$$.type = FLOAT_TYPE;
			gen_code(output,GET_FLOAT,pos);
		    }
		    else if(var[var_index][used_scope].type==FLOAT_ARRAY_TYPE||var[var_index][used_scope].type==INT_ARRAY_TYPE)
		    {
			char buf[1000];
			sprintf(buf,"variable %s was declared as array",$1);
			yyerror(buf);
		    }
		}
		else if(var_index==0)
		{
		    yyerror("invlid ID name");
		}
		else
		{
		    $$.type = UNKNOWN_TYPE;
		    yyerror("undeclared variable");
		}
	    }
	   }
	   | ID '[' arithExpr arrayindex']'
	   {
	     int var_index = CheckDeclared($1,scope,NULL);
	     if(var_index>=1)
	     {
		if($3.type == INT_TYPE)
		{
		    int used_scope = 0;
		    int array_pointer = find_allocated_space(var_num,var_index,scope,&used_scope);
		    Variable temp;
		    if(var[var_index][used_scope].type==INT_ARRAY_TYPE||temp.type==CHAR_ARRAY_TYPE)
		    {
			$$.type = INT_TYPE;
			gen_code_get_array(output,array_pointer,temp.array_start,temp.array_end,INT_TYPE,temp.dim);
		    }
		    else if(var[var_index][used_scope].type==FLOAT_ARRAY_TYPE)
		    {
			$$.type = FLOAT_TYPE;
			gen_code_get_array(output,array_pointer,temp.array_start,temp.array_end,FLOAT_TYPE,temp.dim);
		    }
		    else
		    {
			char buf[1000];
			sprintf(buf,"variable %s was not declared as array",$1);
			yyerror(buf);
		    }
		}
	     }
	     else
	     {
		yyerror("undeclared variable");
	     }
	   }
	   | ID '(' functionArg ')'
	   {
	     int f_index = -1;
	     int i;
	     for(i=0;i<function_num;i++)
	     {
		if(strcmp(function[i].name,$1)==0)
		{
		    f_index = i;
		    break;
		}
	     }
	     if(f_index==-1)
	     {
		yyerror("undeclared function name");
	     }
	     else
	     {
		$$.type = function[f_index].return_type;
		if(function[f_index].param_n!=$3.arg_n)yyerror("wrong number of arguments");
		for(i<function[f_index].param_n-1;i>=0;i--)
		{
		    if(function[f_index].param_type[i]!=$3.type[i])yyerror("wrong argument type");
		    if(function[f_index].param_type[i]==INT_TYPE)
		    {
			fprintf(output,"\tI__0 = POP__S();\n");//arg_value
			fprintf(output,"\tI__1 = %d;\n",function[f_index].param_addr[i]);
			fprintf(output,"\tASSET__S(I__1,I__0);\n");
		    }
		    else
		    {
			fprintf(output,"\tF__0 = FPOP__S();\n");//arg_value
			fprintf(output,"\tI__1 = %d;\n",function[f_index].param_addr[i]);
		    fprintf(output,"\tFASSET__S(I__1,F__0);\n");
		    }
		}
		fprintf(output,"\t%s();\n",$1);
		if(function[f_index].return_type==INT_TYPE)
		{
		    fprintf(output,"\tI__0 = AVAL__S(%d);\n",function[f_index].return_addr);
		    fprintf(output,"\tPUSH__S(I__0);\n");
		}
		else if(function[f_index].return_type==FLOAT_TYPE)
		{
		    fprintf(output,"\tF__0 = FAVAL__S(%d);\n",function[f_index].return_addr);
		    fprintf(output,"\tFPUSH__S(F__0);\n");
		}
		else
		{
		    yyerror("function has no return type");
		}
	     }
	   }
	   ;

logicExpr  : '(' logicExpr ')'
	   {
	     $$ = $2;
	   }
	   | logicExpr OR logicExpr
	   {
	     int max = $1;
	     if($3>$1)max=$3;
	     $$ = max;
	     gen_code(output,LOGIC_OR,0);
	   }
	   | logicExpr AND logicExpr
	   {
	     int max = $1;
	     if($3>$1)max=$3;
	     $$ = max;
	     gen_code(output,LOGIC_AND,0);
	   }
	   | NOT logicExpr
	   {
	     $$ = $2;
	     gen_code(output,LOGIC_NOT,0);
	   }
	   | arithExpr GREATERTHAN arithExpr
	   {
	     if($1.type==$3.type)
	     {
		if($1.type==INT_TYPE)
		{
		    $$ = gen_code(output,GREATER_THAN_INT,0);
		}
		else
		{
		    $$ = gen_code(output,GREATER_THAN_FLOAT,0);
		}
	     }
	     else
	     {
		yyerror("invalid comparison");
	     }
	   }
	   | arithExpr LESSTHAN arithExpr
	   {
	     if($1.type==$3.type)
	     {
		if($1.type==INT_TYPE)
		{
		    $$ = gen_code(output,LESS_THAN_INT,0);
		}
		else
		{
		    $$ = gen_code(output,LESS_THAN_FLOAT,0);
		}
	     }
	     else
	     {
		yyerror("invalid comparison");
	     }
	   }
	   | arithExpr GREATERTHANEQ arithExpr
	   {
	     if($1.type==$3.type)
	     {
		if($1.type==INT_TYPE)
		{
		    $$ = gen_code(output,GREATER_THAN_EQ_INT,0);
		}
		else
		{
		    $$ = gen_code(output,GREATER_THAN_EQ_FLOAT,0);
		}
	     }
	     else
	     {
		yyerror("invalid comparison");
	     }
	   }
	   | arithExpr LESSTHANEQ arithExpr
	   {
	     if($1.type==$3.type)
	     {
		if($1.type==INT_TYPE)
		{
		    $$ = gen_code(output,LESS_THAN_EQ_INT,0);
		}
		else
		{
		    $$ = gen_code(output,LESS_THAN_EQ_FLOAT,0);
		}
	     }
	     else
	     {
		yyerror("invalid comparison");
	     }
	   }
	   | arithExpr EQUAL arithExpr
	   {
	     if($1.type==$3.type)
	     {
		if($1.type==INT_TYPE)
		{
		    $$ = gen_code(output,CMP_EQUAL_INT,0);
		}
		else
		{
		    $$ = gen_code(output,CMP_EQUAL_INT,0);
		}
	     }
	     else
	     {
		yyerror("invalid comparison");
	     }
	   }
	   | arithExpr NOTEQUAL arithExpr
	   {
	     if($1.type==$3.type)
	     {
		if($1.type==INT_TYPE)
		{
		    $$ = gen_code(output,CMP_NOT_EQUAL_INT,0);
		}
		else
		{
		    $$ = gen_code(output,CMP_NOT_EQUAL_FLOAT,0);
		}
	     }
	     else
	     {
		yyerror("invalid comparison");
	     }
	   }
	   ;
%%

int yyerror(const char message[]) {   
    //printf("failed at line:%d %s\n",yylineno,message);
    //return -1;
    int err_no = 0;
    if(strstr(message,"ID")!=NULL)
    {
	err_no = INVALID_ID_NAME;
	if(err_no!=prev_err_no || prev_err_lineno!=yylineno)
	{
	    printf("%d: invalid ID name\n",yylineno);
	}
    }
    else if(strstr(message,"syntax")!=NULL)
    {
	err_no = SYNTAX_ERROR;
	if(err_no!=prev_err_no || prev_err_lineno!=yylineno)
	{
	    printf("%d: syntax error\n",yylineno);
	}
    }
    else if(strcmp(message,"undeclared variable")==0)
    {
	err_no = UNDECLARED_VARIABLE;
	if(err_no!=prev_err_no || prev_err_lineno!=yylineno)
	{
	    printf("%d: %s\n", yylineno, message);
	}
    }
    else if(strcmp(message,"variable declared twice")==0)
    {
	err_no = VARIABLE_DECLARED_TWICE;
	if(err_no!=prev_err_no || prev_err_lineno!=yylineno)
	{
	    printf("%d: %s\n", yylineno, message);
	}
    }
    else if(strcmp(message,"mismatched data type")==0)
    {
	err_no = MISMATCHED_DATA_TYPE;
	if(err_no!=prev_err_no || prev_err_lineno!=yylineno)
	{
	    printf("%d: %s\n", yylineno, message);
	}
    }
    else if(strcmp(message,"invalid constant")==0)
    {
	err_no = INVALID_CONSTANT;
	if(err_no!=prev_err_no || prev_err_lineno!=yylineno)
	{
	    printf("%d: %s\n", yylineno, message);
	}
    }
    else if(strstr(message,"was not declared as array")!=NULL)
    {
	err_no = VARIABLE_WAS_NOT_ARRAY_TYPE;
	if(err_no!=prev_err_no || prev_err_lineno!=yylineno)
	{
	    printf("%d: %s\n", yylineno, message);
	}
    }
    else if(strcmp(message,"invalid array index")==0)
    {
	err_no = INVALID_ARRAY_INDEX;
	if(err_no!=prev_err_no || prev_err_lineno!=yylineno)
	{
	    printf("%d: %s\n", yylineno, message);
	}
    }
    else if(strstr(message,"was declared as array")!=NULL)
    {
	err_no = VARIABLE_WAS_ARRAY_TYPE;
	if(err_no!=prev_err_no || prev_err_lineno!=yylineno)
	{
	    printf("%d: %s\n", yylineno, message);
	}
    }
    else if(strcmp(message,"invalid comparison")==0)
    {
	err_no = INVALID_COMPARISON;
	if(err_no!=prev_err_no || prev_err_lineno!=yylineno)
	{
	    printf("%d: %s\n", yylineno, message);
	}
    }
    else if(strcmp(message,"undeclared function name")==0)
    {
	err_no = UNDECLARED_FUNCTION_NAME;
	if(err_no!=prev_err_no || prev_err_lineno!=yylineno)
	{
	    printf("%d: %s\n", yylineno, message);
	}
    }
    else if(strcmp(message,"function has no return type")==0)
    {
	err_no = FUNCTION_HAS_NO_RETURN_TYPE;
	if(err_no!=prev_err_no || prev_err_lineno!=yylineno)
	{
	    printf("%d: %s\n", yylineno, message);
	}
    }
    else if(strcmp(message,"wrong argument type")==0)
    {
	err_no = WRONG_ARGUMENT_TYPE;
	if(err_no!=prev_err_no || prev_err_lineno!=yylineno)
	{
	    printf("%d: %s\n", yylineno, message);
	}
    }
    else if(strcmp(message,"wrong number of arguments")==0)
    {
	err_no = WRONG_NUMBER_OF_ARGUMENTS;
	if(err_no!=prev_err_no || prev_err_lineno!=yylineno)
	{
	    printf("%d: %s\n", yylineno, message);
	}
    }
    else
    {
	printf("%s",message,yylineno);
    }
    prev_err_no = err_no;
    prev_err_lineno = yylineno;
    return 0;
}

int CheckDeclared(char* id, int scope, int* status)
{
    if(in_function>=0)
    {
	if(strcmp(function[in_function].name,id)==0)return 0;
    }
    int i;
    int declared = 0;
    int var_index = -1;
    for(i=0;i<var_num;i++)
    {
	if(strcmp(var_name[i],id)==0)
	{
	    declared = 1;
	    var_index = i;
	    break;
	}
    }
    if(declared==1)
    {
	if(var[var_index][scope].addr==0)
	{
	    if(status!=NULL)*status = UNDECLARED_IN_THIS_SCOPE;
	    return var_index;
	}
	else
	{
	    if(status!=NULL)*status = DECLARED_IN_THIS_SCOPE;
	    return var_index;
	}
    }
    return -1;
}

main(int argc,char* argv[]) {
  int i, executable = 1;
  const char *in_name = NULL, *out_name = NULL;
  extern FILE *yyin;
  output = stdout;
  for (i = 1; i < argc; i++) 
  {
    if (!strcmp(argv[i], "-o")) 
    {
      i++;
      if (i < argc)
        out_name = argv[i];
    } else if (!strcmp(argv[i], "-S"))
      executable = 0;
    else
      in_name = argv[i];
  }
  if (in_name)
    yyin = fopen(in_name, "r");
  if (executable && !out_name)
    out_name = "p.out";
    
 /* intermediate code */
  if (executable)
  output = fopen("_tmp.c", "w+");
  else if (out_name)
  output = fopen(out_name, "w+");

  /*start parsing*/
  i = yyparse();
  if (i == 0) 
  { 
    if (executable) 
    {
      fclose(output);
      char buf[99];
      sprintf(buf, "gcc _tmp.c -DMAX__S=99999 -include cmm.c -o %s", out_name);
      system(buf);
    }
  } 
  else 
  {
    /* error messages */
  }
}
